#pragma once

#include "Transform.h"

namespace krt {

	class KRT_API BBox {
	public:
		BBox(const glm::vec3& p = { KRTMath::FloatMax,KRTMath::FloatMax,KRTMath::FloatMax })
			:pMin(p), pMax(p)
		{}

		BBox(const glm::vec3& pmin, const glm::vec3& pmax)
			:pMin(pmin), pMax(pmax)
		{}

		static BBox Union(const BBox& a, const BBox& b) {
			BBox reBox = a;
			reBox.pMin.x = glm::min(a.pMin.x, b.pMin.x);
			reBox.pMin.y = glm::min(a.pMin.y, b.pMin.y);
			reBox.pMin.z = glm::min(a.pMin.z, b.pMin.z);
			reBox.pMax.x = glm::max(a.pMax.x, b.pMax.x);
			reBox.pMax.y = glm::max(a.pMax.y, b.pMax.y);
			reBox.pMax.z = glm::max(a.pMax.z, b.pMax.z);
			return reBox;
		}

		// is 2 box overlaped
		bool Overlaps(const BBox& b) const {
			bool x = (pMax.x >= b.pMin.x) && (pMin.x <= b.pMax.x);
			bool y = (pMax.y >= b.pMin.y) && (pMin.y <= b.pMax.y);
			bool z = (pMax.z >= b.pMin.z) && (pMin.z <= b.pMax.z);
			return (x && y && z);
		}

		// is point inside
		bool Inside(const glm::vec3& p) const {
			return (
				p.x >= pMin.x && p.x <= pMax.x &&
				p.x >= pMin.y && p.y <= pMax.y &&
				p.z >= pMin.z && p.z <= pMax.z
				);
		}

		// expand by single value (cube)
		void Expand(float delta) {
			pMin -= glm::vec3(delta, delta, delta);
			pMax += glm::vec3(delta, delta, delta);
		}

		float SurfaceArea() const {
			auto d = pMax - pMin;
			return 2.f * (d.x * d.y + d.x * d.z + d.y + d.z);
		}

		float Volume() const {
			auto d = pMax - pMin;
			return d.x * d.y * d.z;
		}

		// max extent axis index
		int MaxExtent() const {
			auto diag = pMax - pMin;
			if (diag.x > diag.y && diag.x > diag.z) return 0;
			if (diag.y > diag.z) return 1;
			return 2;
		}

		const glm::vec3& operator[](int i) const { return i == 0 ? pMin : pMax; }
		glm::vec3& operator[](int i) { return i == 0 ? pMin : pMax; }

		glm::vec3 Lerp(const glm::vec3& x) const {
			return KRTMath::Lerp(pMin, pMax, x);
		}

		glm::vec3 Offset(const glm::vec3& p) const {
			return {
				(p.x - pMin.x) / (pMax.x - pMin.x),
				(p.y - pMin.y) / (pMax.y - pMin.y),
				(p.z - pMin.z) / (pMax.z - pMin.z)
			};
		}

		void BoundingSphere(glm::vec3& center, float& radius) const {
			center = .5f * pMin + .5f * pMax;
			radius = Inside(center) ? glm::distance(center, pMax) : .0f;
		}

	public:
		glm::vec3 pMin, pMax;
	};

}